package org.loofer.ext.utils.byteunits;

import java.text.DecimalFormat;
import java.text.NumberFormat;

public enum BitUnit implements ByteUnit {
    BITS {
        public long convert(long sourceCount, BitUnit sourceUnit) {
            return sourceUnit.toBits(sourceCount);
        }

        public long toBytes(long count) {
            return count / 8;
        }

        public long toBits(long count) {
            return count;
        }

        public long toKilobits(long count) {
            return count / BitUnit.KB;
        }

        public long toMegabits(long count) {
            return count / BitUnit.MB;
        }

        public long toGigabits(long count) {
            return count / BitUnit.GB;
        }

        public long toTerabits(long count) {
            return count / BitUnit.TB;
        }

        public long toPetabits(long count) {
            return count / BitUnit.PB;
        }
    },
    KILOBITS {
        public long convert(long sourceCount, BitUnit sourceUnit) {
            return sourceUnit.toKilobits(sourceCount);
        }

        public long toBytes(long count) {
            return BitUnit.KBYTE * count;
        }

        public long toBits(long count) {
            return UnitUtil.multiply(count, BitUnit.KB, 9223372036854775L);
        }

        public long toKilobits(long count) {
            return count;
        }

        public long toMegabits(long count) {
            return count / BitUnit.KB;
        }

        public long toGigabits(long count) {
            return count / BitUnit.MB;
        }

        public long toTerabits(long count) {
            return count / BitUnit.GB;
        }

        public long toPetabits(long count) {
            return count / BitUnit.TB;
        }
    },
    MEGABITS {
        public long convert(long sourceCount, BitUnit sourceUnit) {
            return sourceUnit.toMegabits(sourceCount);
        }

        public long toBytes(long count) {
            return BitUnit.MBYTE * count;
        }

        public long toBits(long count) {
            return UnitUtil.multiply(count, BitUnit.MB, 9223372036854L);
        }

        public long toKilobits(long count) {
            return UnitUtil.multiply(count, BitUnit.KB, 9223372036854775L);
        }

        public long toMegabits(long count) {
            return count;
        }

        public long toGigabits(long count) {
            return count / BitUnit.KB;
        }

        public long toTerabits(long count) {
            return count / BitUnit.MB;
        }

        public long toPetabits(long count) {
            return count / BitUnit.GB;
        }
    },
    GIGABITS {
        public long convert(long sourceCount, BitUnit sourceUnit) {
            return sourceUnit.toGigabits(sourceCount);
        }

        public long toBytes(long count) {
            return BitUnit.GBYTE * count;
        }

        public long toBits(long count) {
            return UnitUtil.multiply(count, BitUnit.GB, 9223372036L);
        }

        public long toKilobits(long count) {
            return UnitUtil.multiply(count, BitUnit.MB, 9223372036854L);
        }

        public long toMegabits(long count) {
            return UnitUtil.multiply(count, BitUnit.KB, 9223372036854775L);
        }

        public long toGigabits(long count) {
            return count;
        }

        public long toTerabits(long count) {
            return count / BitUnit.KB;
        }

        public long toPetabits(long count) {
            return count / BitUnit.MB;
        }
    },
    TERABITS {
        public long convert(long sourceCount, BitUnit sourceUnit) {
            return sourceUnit.toTerabits(sourceCount);
        }

        public long toBytes(long count) {
            return BitUnit.TBYTE * count;
        }

        public long toBits(long count) {
            return UnitUtil.multiply(count, BitUnit.TB, 9223372);
        }

        public long toKilobits(long count) {
            return UnitUtil.multiply(count, BitUnit.GB, 9223372036L);
        }

        public long toMegabits(long count) {
            return UnitUtil.multiply(count, BitUnit.MB, 9223372036854L);
        }

        public long toGigabits(long count) {
            return UnitUtil.multiply(count, BitUnit.KB, 9223372036854775L);
        }

        public long toTerabits(long count) {
            return count;
        }

        public long toPetabits(long count) {
            return count / BitUnit.KB;
        }
    },
    PETABITS {
        public long convert(long sourceCount, BitUnit sourceUnit) {
            return sourceUnit.toPetabits(sourceCount);
        }

        public long toBytes(long count) {
            return BitUnit.PBYTE * count;
        }

        public long toBits(long count) {
            return UnitUtil.multiply(count, BitUnit.PB, 9223);
        }

        public long toKilobits(long count) {
            return UnitUtil.multiply(count, BitUnit.TB, 9223372);
        }

        public long toMegabits(long count) {
            return UnitUtil.multiply(count, BitUnit.GB, 9223372036L);
        }

        public long toGigabits(long count) {
            return UnitUtil.multiply(count, BitUnit.MB, 9223372036854L);
        }

        public long toTerabits(long count) {
            return UnitUtil.multiply(count, BitUnit.KB, 9223372036854775L);
        }

        public long toPetabits(long count) {
            return count;
        }
    };
    
    private static final long B = 1;
    private static final long BYTE = 8;
    private static final long GB = 1000000000;
    private static final long GBYTE = 125000000;
    private static final long KB = 1000;
    private static final long KBYTE = 125;
    private static final long MAX = Long.MAX_VALUE;
    private static final long MB = 1000000;
    private static final long MBYTE = 125000;
    private static final long PB = 1000000000000000L;
    private static final long PBYTE = 125000000000000L;
    private static final long TB = 1000000000000L;
    private static final long TBYTE = 125000000000L;
    private static final String[] UNITS = null;

    static {
        UNITS = new String[]{"b", "Kb", "Mb", "Gb", "Tb", "Pb"};
    }

    public static String format(long bits) {
        return format(bits, new DecimalFormat("#,##0.#"));
    }

    public static String format(long bits, String pattern) {
        return format(bits, new DecimalFormat(pattern));
    }

    public static String format(long bits, NumberFormat format) {
        if (bits < 0) {
            throw new IllegalArgumentException("bits < 0: " + bits);
        }
        int unitIndex = 0;
        double count = (double) bits;
        while (count >= 1000.0d && unitIndex < UNITS.length - 1) {
            count /= 1000.0d;
            unitIndex++;
        }
        return format.format(count) + ' ' + UNITS[unitIndex];
    }

    public long convert(long sourceCount, BitUnit sourceUnit) {
        throw new AbstractMethodError();
    }

    public long toBits(long count) {
        throw new AbstractMethodError();
    }

    public long toKilobits(long count) {
        throw new AbstractMethodError();
    }

    public long toMegabits(long count) {
        throw new AbstractMethodError();
    }

    public long toGigabits(long count) {
        throw new AbstractMethodError();
    }

    public long toTerabits(long count) {
        throw new AbstractMethodError();
    }

    public long toPetabits(long count) {
        throw new AbstractMethodError();
    }
}
